package eval.validation;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import eval.deploy.IDemandManager;
import eval.deploy.IDeployManager;
import eval.engine.IPlacement;
import eval.host.Host;
import eval.utilities.configuration.ExperimentConfiguration;
import eval.vm.IVMManager;
import eval.vm.VirtualMachine;

/**
 * this class perform some basic checks at the end of the experiment to make sure
 * we made legal actions during the test.
 * 
 * @author Amit Peled   <amit.kapina@gmail.com>
 * @author Igal Kaufman <igal.kaufman@gmail.com>
 */
public class SanityCheck {

	private ExperimentConfiguration configuration = ExperimentConfiguration.getSingleton();

	private IDemandManager demandManager;


	/**
	 * 
	 * @Constructor
	 * @param deployManager
	 * @param vmManager
	 * @param demandManager
	 */
	public SanityCheck(IDeployManager deployManager, IVMManager vmManager ,IDemandManager demandManager) {
		this.demandManager = demandManager;
	}




	/**
	 * test if the placement are correct
	 *  
	 * @param curPlacement  the placement to test
	 * @param prevPlacement last placement
	 * @param tick the current cycle
	 * @param out the file to write to
	 * @return true if found error, false otherwise
	 * @throws IOException
	 */
	public Boolean runSaintyCheck(IPlacement curPlacement, IPlacement prevPlacement, int tick,FileWriter out) throws IOException{
		boolean isThereAnyError = false;
		Map<Host, List<VirtualMachine>> CURvmMapping =  curPlacement.getvmMapping();
		List<VirtualMachine> VMSrequestedTobedeployed = curPlacement.getVMSrequestedTobedeployed();
		List<VirtualMachine> RejectedVMS = curPlacement.getRejectedVMS();
		List<VirtualMachine> PlacedVMS = curPlacement.getPlacedList();
		List<VirtualMachine> RemovedList = curPlacement.getRemovedList();
		List<VirtualMachine> PlacedAndRejected = new ArrayList<VirtualMachine>();
		PlacedAndRejected.addAll(PlacedVMS);
		PlacedAndRejected.addAll(RejectedVMS);
		if(!VMSrequestedTobedeployed.containsAll(PlacedAndRejected) ||  !(PlacedAndRejected.containsAll(VMSrequestedTobedeployed)) ){
			isThereAnyError = true;
			out.write("Some vm's from the deploy set are ignored.at tick: " + tick +"\n");
		}
		if(VMSrequestedTobedeployed.size() != (RejectedVMS.size() + PlacedVMS.size())){
			isThereAnyError = true;
			out.write("VMSrequestedTobedeployed != RejectedVMS + PlacedVMS at tick: "+tick+"\n");
		}
		if(curPlacement.getTotalCloudCPULoad() > curPlacement.getTotalCloudCpuDemand()){
			isThereAnyError = true;
			out.write("TotalCloudCPULoad > TotalCloudCpuDemand at tick:"+tick+"\n");
		}
		if(curPlacement.getTotalCloudCpuDemand() != curPlacement.getTotalSatisfiedCPUDemand() + curPlacement.getTotalUnSatisfiedCPUDemand()){
			isThereAnyError = true;
			out.write("TotalCloudCpuDemand != TotalSatisfiedCPUDemand + TotalUnSatisfiedCPUDemand at tick: "+tick+"\n");
		}

		if(configuration.totalNumberOfHosts != curPlacement.getNumberOfOpenHosts() + curPlacement.getNumberOfClosedHosts() + curPlacement.getNumberOfCandidatesForClosingHosts()){
			isThereAnyError = true;
			out.write("Error(#5):total number of hosts is wrong. at tick: " + tick + "\n");
		}

		if(curPlacement.getNumberOfTurnOffHosts() > 0 && curPlacement.getNumberOfTurnOnHosts() > 0){
			isThereAnyError = true;
			out.write("Error(#4): we try to open and close at the same placement. " + tick  + "\n" );
		}


		int NumberOfVMsInCloud = 0;
		for(Integer hostID: curPlacement.getCloudMap().keySet()){
			NumberOfVMsInCloud += curPlacement.getCloudMap().get(hostID).size();
		}

		if(curPlacement.getNumberOfVMsAtCloud() !=  NumberOfVMsInCloud){
			isThereAnyError = true;
			out.write("Error(#6):the total number of vm's at cloud are wrong. at tick: " + tick + "\n");
		}

		// iterates over host within the cloud
		int MemConsumptionInHost;

		for (Host host : CURvmMapping.keySet()) {

			MemConsumptionInHost = 0;
			// iterates over vms within the host
			for (VirtualMachine vm : CURvmMapping.get(host)) {
				MemConsumptionInHost += demandManager.getRealDemand(vm,tick
						).getMEMDemandAtTick(tick);

				if(RemovedList.contains(vm)){
					isThereAnyError = true;
					out.write("Error(#2): vm: " + vm.getIdentifier() + " is spouse to be removed but still appear in cloud.found at tick: " + tick + "\n");
				}
				if(RejectedVMS.contains(vm)){
					isThereAnyError = true;
					out.write("Error(#3): vm: " + vm.getIdentifier() + " is spouse to be rejected but appear in cloud.found at tick: " + tick + "\n");
				}

			}//end: iterates over vms within the host

			if(MemConsumptionInHost > host.getHostCapacity().getMemCapacity()){
				isThereAnyError = true;
				out.write("Error(#1): the total memory consumption of vm's in host number "
						+ host.getHostId() +" is bigger than memory capaity.tick number: " + tick + "\n");

			}
		}
		return isThereAnyError;
	}

}